package com.ywy.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ywy.entity.Post;
import com.ywy.mapper.PostMapper;
import com.ywy.service.PostService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ywy.utils.RedisUtil;
import com.ywy.vo.PostVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 博客文章 服务实现类
 * </p>
 *
 * @author ywy
 * @since 2021-12-24
 */
@Service
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements PostService {
    @Autowired
    private PostMapper postMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Cacheable(cacheNames = "cache_post", key = "'page_' + #page.current + '_' + #page.size + '_query_' + #categoryId + '_' + #userId + '_' +  #level + '_' + #recommend + '_' + #order")
    @Override
    public IPage listByPage(Page page, Integer categoryId, Integer userId, Integer level, Boolean recommend, String order) {
        if (level == null) level = -1;
        QueryWrapper queryWrapper = new QueryWrapper<Post>()
                .eq(categoryId != null, "category_id", categoryId)
                .eq(userId != null, "user_id", userId)
                .eq(level == 0, "level", 0)
                .gt(level > 0, "level", 0)
                .orderByDesc(order != null, order);
        return postMapper.listByPage(page, queryWrapper);
    }

    @Override
    public PostVo getDetail(Integer id) {
        return postMapper.getDetail(id);
    }

    @Override
    public void initWeekRank() {
        // 查询最近7天的所有文章
        QueryWrapper<Post> queryWrapper = new QueryWrapper<Post>().ge("create_time", DateUtil.offsetDay(new Date(), -7))
                .select("id, title, user_id, comment_count, view_count, create_time");
        List<Post> postList = this.list(queryWrapper);
        // 初始化文章评论量
        for (Post post : postList) {
            // 按每天日期缓存文章评论数量到有序集合中，文章id作为值，评论数量作为分数
            String key = "day_rank:" + DateUtil.format(post.getCreateTime(), DatePattern.PURE_DATE_PATTERN);
            redisUtil.zadd(key, post.getId(), post.getCommentCount());
            // 设置有效期，7天后自动过期
            long diffDays = DateUtil.between(new Date(), post.getCreateTime(), DateUnit.DAY);
            long expireTime = (7 - diffDays) * 24 * 60 * 60;
            redisUtil.expire(key, expireTime);

            // 缓存文章基本信息（id、标题、评论数量、作者）
            this.hashCachePostInfo(post, expireTime);
        }

        // 合并最近7天的文章的评论量
        this.unionLast7DaysForWeekRank();
    }

    @Override
    public void updateWeekRank(Integer postId, boolean isIncr) {
        // 修改今天的文章的分数
        String currentKey = "day_rank:" + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        redisUtil.zincr(currentKey, postId, isIncr? 1: -1);

        // 根据id获取博客文章
        Post post = this.getById(postId);

        // 缓存这篇文章的基本信息
        long between = DateUtil.between(new Date(), post.getCreateTime(), DateUnit.DAY);
        long expireTime = (7 - between) * 24 * 60 * 60;
        this.hashCachePostInfo(post, expireTime);

        // 重新合并最近7天的文章评论量
        this.unionLast7DaysForWeekRank();
    }

    @Override
    public void updateViewCount(PostVo post) {
        String key = "rank_post:" + post.getId();
        // 从缓存中获取阅读量
        Integer viewCount = (Integer) redisUtil.hget(key, "post:viewCount");

        // 阅读量加一
        if (viewCount != null) {
            post.setViewCount(viewCount + 1);
        } else {
            // 如果没有，则先从实体里面获取，再加一
            post.setViewCount(post.getViewCount() + 1);
        }

        // 同步到缓存里面
        redisUtil.hset(key, "post:viewCount", post.getViewCount());
    }

    /**
     * 缓存文章的基本信息
     * @param post
     * @param expireTime
     */
    private void hashCachePostInfo(Post post, long expireTime) {
        String key = "rank_post:" + post.getId();
        boolean isExist = redisUtil.exists(key);
        if (!isExist) {
            redisUtil.hset(key, "post:title", post.getTitle(), expireTime);
            redisUtil.hset(key, "post:commentCount", post.getCommentCount(), expireTime);
            redisUtil.hset(key, "post:viewCount", post.getViewCount(), expireTime);
        }
    }

    /**
     * 合并最近7天的文章的评论量，得到本周热议数据
     */
    private void unionLast7DaysForWeekRank() {
        String key = "week_rank";
        String dayKey = "day_rank:" + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        List<String> otherKeys = new ArrayList<>();
        // 计算前6天的文章的评论数量key
        Date date = new Date();
        for (int i = -6; i < 0; i++) {
            otherKeys.add("day_rank:" + DateUtil.format(DateUtil.offsetDay(date, i), DatePattern.PURE_DATE_PATTERN));
        }
        redisUtil.zunionstore(dayKey, otherKeys, key);
    }
}
